#include "Cengine.h"
#include "CSprite.h"
#include <SDL.h>
#include <string>

#include "CGameObject.h"
#include "myexception.h"
#include "CRenderer.h"
#include "CDirectRenderer.h"

// #define FULLSCREEN

using namespace std;

Uint32 FpsCallback(Uint32 interval, void *param)
{
	printf("FPS: %ld\n",FPS); 
	FPS = 0;
	return interval;
}


Cengine::Cengine(const size_t screenw, const size_t screenh)
: ScreenWidth(screenw)
, ScreenHeight(screenh)
, IsMinimized(false)
, ShouldQuit(false)
, TickInterval(10)
, NextTime(0)
, Map(0)
, SpriteManager( new CSpriteManager)
{
	FpsTimerId = 0;
}

Cengine::~Cengine()
{
	fprintf(stderr, "Exit game.\n");
	delete SpriteManager;
	delete Map;
	delete Renderer;

	SDL_RemoveTimer(FpsTimerId);
	SDL_Quit();
}

void Cengine::Init()
{
	InitVideo();
	SpriteManager->LoadSprites();
	Map = new CMap("Content/map.txt");

	TopLeftCornerMapX = (ScreenWidth - Map->GetWidth() * BLOCK_SIZE_X) / 2;
	printf("screen = (%ld, %ld)\n", ScreenWidth, ScreenHeight);
	printf(" %ld x %ld\n", Map->GetWidth(), Map->GetHeight());
	TopLeftCornerMapY = (ScreenHeight - Map->GetHeight() * (BLOCK_SIZE_Y + BLOCK_SIZE_Z)) / 2;
	Renderer = new CDirectRenderer(Screen, SpriteManager, ScreenWidth, ScreenHeight, TopLeftCornerMapX, TopLeftCornerMapY);

}

void Cengine::InitVideo()
{
	if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) < 0)
		throw MyException("Unable to init SDL: ");

	Screen = SDL_SetVideoMode(ScreenWidth, ScreenHeight, 32, SDL_HWSURFACE | SDL_DOUBLEBUF
#ifdef FULLSCREEN
		| SDL_FULLSCREEN
#endif
		);
	if (!Screen)
		throw MyException("Unable to set up video: ");

	FpsTimerId = SDL_AddTimer(1000, FpsCallback, 0);
}

void Cengine::Run()
{
	while (!ShouldQuit)
	{
		HandleInput();

		DoThink();
		DoRender();
	}
}

void Cengine::HandleInput()
{
	SDL_Event event;
	while (SDL_PollEvent(&event))
	{
		switch (event.type)
		{
		case SDL_KEYDOWN:
			if (event.key.keysym.sym == SDLK_ESCAPE)
			{
				ShouldQuit = true;
				break;
			}
			break;

		case SDL_MOUSEMOTION:
			/*
			printf("mouse moved: (%d, %d) to (%d,%d)\n",
				event.motion.xrel,
				event.motion.yrel,
				event.motion.x,
				event.motion.y);
			*/
			MouseBrick(event.motion.x, event.motion.y);
			// printf("brick: %d, %d\n", SelectorPosition.first, SelectorPosition.second);
			break;

		case SDL_MOUSEBUTTONDOWN:
			if (Selector.IsValid())
			{
				if (Selector.Get())
				{
					// we have brick on selector...
					if (Map->CouldSetBlock(Selector.GetX(), Selector.GetY()))
					{
						Map->SetBlock(Selector.Get(), Selector.GetX(), Selector.GetY());
						Selector.Peek(0);
					}
				} else
				{
					// selector is empty
					if (Map->CouldGetBlock(Selector.GetX(), Selector.GetY()))
						Selector.Peek(Map->GetBlock(Selector.GetX(), Selector.GetY()));
				}
			}
			
			break;

		case SDL_MOUSEBUTTONUP:
			break;

		case SDL_QUIT:
			ShouldQuit = true;
			break;

		case SDL_ACTIVEEVENT:
			if (event.active.state & SDL_APPACTIVE)
			{
				if (event.active.gain)
					IsMinimized = false;
				else
					IsMinimized = true;
			}
			break;
		}
	}
}

void Cengine::WaitMessage()
{
}

void Cengine::DoThink()
{
	// Do some work...
	SDL_Delay(TimeLeft());
}

void Cengine::DoRender()
{
	// DrawMap();
	// Renderer->Redraw();

	CBlockView a(B_BROWN_BLOCK);
	a.AddShadowWest();
	a.AddShadowNorth();
	//a.AddShadowSideWest();
	a.AddShadowSouth();
	CBlockView b(B_GRASS_BLOCK);
	b.AddShadowSouthWest();

	SpriteManager->DrawBlock(a, Screen, 10, 10);
	SpriteManager->DrawBlock(b, Screen, 10 + 100, 10);

	SDL_Flip(Screen);

    // clear screen;
    SDL_FillRect(Screen, 0, 0);
	
	++FPS;
}

void Cengine::DrawMap()
{
	for (size_t j = 0; j < Map->GetHeight(); ++j)
		for (size_t i = 0; i < Map->GetWidth(); ++i)
		{
			vector<CGameObject*> ol = Map->At(i,j);
			int k = 0;
			for (vector<CGameObject*>::const_iterator it = ol.begin();
				it != ol.end(); ++it, ++k)
			{
				CGameObject* o = *it;
				Renderer->DrawSprite(o->GetSpriteType(), 
					BLOCK_SIZE_X * i + o->DrawPosX(), 
					BLOCK_SIZE_Y * j - BLOCK_SIZE_Z * k + o->DrawPosY());
				
				Map->Shadow(TmpShadows, i, j, k);
				if (TmpShadows.size())
					for (vector<char>::const_iterator its = TmpShadows.begin(); 
						its != TmpShadows.end(); ++its)
					{
/*						if (*its == SHADOW_SOUTH_ON_NORTH && *it == ol.back())
							Renderer->DrawSprite(SELECTOR,
								BLOCK_SIZE_X * i + o->DrawPosX(),
								BLOCK_SIZE_Y * (j - 1) - BLOCK_SIZE_Z * k + o->DrawPosY());
						else*/
							Renderer->DrawSprite(static_cast<SpriteType>(*its),
								BLOCK_SIZE_X * i + o->DrawPosX(), 
								BLOCK_SIZE_Y * j - BLOCK_SIZE_Z * k + o->DrawPosY());
					}
			}
			if (Selector.GetX() == i && Selector.GetY() == j)
			{
				
				CGameObject* so = Selector.Get();
				if (so)
				{
					if (Map->CouldSetBlock(i,j))
						Renderer->DrawSprite(SELECTOR,
							BLOCK_SIZE_X * i,
							BLOCK_SIZE_Y * j - BLOCK_SIZE_Z * ol.size());
					Renderer->DrawSprite(so->GetSpriteType(),
						BLOCK_SIZE_X * i,
						BLOCK_SIZE_Y * j - BLOCK_SIZE_Z * (ol.size() + 3));
				}
				if (Map->CouldGetBlock(i,j) && !so)
					Renderer->DrawSprite(SELECTOR,
						BLOCK_SIZE_X * i,
						BLOCK_SIZE_Y * j - BLOCK_SIZE_Z * ol.size());
					
			}
		}
}

bool Cengine::MouseBrick(int x, int y)
{
	// printf("x = %d, y = %d\n", x / BLOCK_SIZE_X, (y - BLOCK_SIZE_Z)/ BLOCK_SIZE_Y );
	x -= TopLeftCornerMapX;
	y -= TopLeftCornerMapY;

	int a = x / static_cast<int>(BLOCK_SIZE_X);
	int b = (y - static_cast<int>(BLOCK_SIZE_Z)) / static_cast<int>(BLOCK_SIZE_Y);

	if (a < 0 || 
		a >= static_cast<int>(Map->GetWidth()) ||
		b < 0 || 
		b >= static_cast<int>(Map->GetHeight()))
	{
		Selector.SetPosition(-1,-1);
		return false;
	}

	Selector.SetPosition(a, b);
	return true;
}
